'use client';

import type {
  ICreatePostHistoryBody,
  IDifference,
  IHistory,
  IPostEditInfo,
  IQueryAllHistoryByPostId,
} from '@/interfaces';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
  createPostHistory,
  queryAllHistoryByPostId,
  queryHistory,
} from '@/services/api';
import Alert from '@/app/[locale]/alert/alert';
import AlertLoad from '@/app/[locale]/alert/load';
import {
  type ChangeEvent,
  type FormEvent,
  type MutableRefObject,
  useEffect,
  useRef,
  useState,
} from 'react';
import { diff_match_patch } from 'diff-match-patch';
import useToast from '@/hooks/useToast';
import type { TBody } from '@/types';
import { flushSync } from 'react-dom';
import classNames from 'classnames';
import sanitizeHtml from 'sanitize-html';
import { formatDate, formatTime, getDiffData, getFilterData } from '@/lib/tool';
import diff from 'microdiff';
import { type QueryKey } from '@tanstack/query-core';
import { useTranslations } from 'use-intl';

export default function History(
  this: any,
  {
    data,
    hideModal,
    setData,
    currentContent,
    currentOverview,
  }: {
    data: IPostEditInfo;
    hideModal: () => void;
    setData: (value: any) => void;
    currentContent: string;
    currentOverview: string;
  },
) {
  const [loadDiffMatchPatch, setLoadDiffMatchPatch] = useState(false);
  const diffMatchPatchRef = useRef<diff_match_patch>();
  const [currentHistoryKey, setCurrentHistoryKey] = useState<string>();
  const [isCreate, setIsCreate] = useState(false);
  const queryKey = ['/forum', '/histories', '/posts', data.basic.id];
  const t = useTranslations('PostEditPage');

  const queryAllHistoryByPostIdQuery = useQuery(queryKey, async (context) => {
    return (await queryAllHistoryByPostId({
      id: context.queryKey[3] + '',
    })) as IQueryAllHistoryByPostId;
  });

  async function onClickHistory(item: string) {
    if (!diffMatchPatchRef.current) {
      await flushSync(async () => {
        setLoadDiffMatchPatch(true);
        const diffMatchPatch = (await import('diff-match-patch')).default;
        diffMatchPatchRef.current = new (diffMatchPatch as any)();
        setLoadDiffMatchPatch(false);
      });
    }

    setIsCreate(false);
    setCurrentHistoryKey(item);
  }

  function onClickCreate() {
    setCurrentHistoryKey(undefined);
    setIsCreate(true);
  }

  if (queryAllHistoryByPostIdQuery.data) {
    const historyList = queryAllHistoryByPostIdQuery.data;

    return (
      <div className="row">
        <div className="col">
          <div
            className={classNames(
              currentHistoryKey ? 'overflow-y-auto' : false,
            )}
          >
            <div className="list-group">
              <div
                className="card border text-muted border-1 mb-3 list-group-item list-group-item-action cursor-pointer"
                onClick={onClickCreate}
              >
                <div className="card-body px-0 py-2">
                  <div className="row">
                    <div className="col-3 m-auto text-center ms-0">
                      <i className="bi bi-plus-circle fs-2 text-secondary"></i>
                    </div>
                    <div className="col-8 align-self-center">
                      <p className="card-title mb-0 text-truncate text-secondary">
                        {t('createVersion')}
                      </p>
                    </div>
                  </div>
                </div>
              </div>

              {Object.keys(historyList).map((item) => {
                return (
                  <div
                    key={item}
                    className={classNames(
                      'card border text-muted border-1 mb-3 list-group-item list-group-item-action cursor-pointer',
                      currentHistoryKey === item ? 'active' : null,
                    )}
                    title={`${t('currentContentVersionCountExists')} ${
                      historyList[item].length
                    }`}
                    onClick={onClickHistory.bind(this, item)}
                  >
                    <div className="card-body px-0 py-2">
                      {loadDiffMatchPatch ? (
                        <div className="hstack text-white">
                          <span
                            style={{ width: '2rem', height: '2rem' }}
                            className="spinner-border spinner-border-sm me-2 flex-shrink-0"
                            role="status"
                            aria-hidden="true"
                          ></span>
                          <span className="text-truncate">
                            {t('loadingInProgress')}...
                          </span>
                        </div>
                      ) : (
                        <div className="row">
                          <div className="col-3 m-auto text-center ms-0">
                            <i
                              className={classNames(
                                'bi bi-clock-history fs-2',
                                currentHistoryKey === item
                                  ? 'text-white'
                                  : 'text-secondary',
                              )}
                            ></i>
                          </div>
                          <div className="col-8 align-self-center">
                            <p
                              className={classNames(
                                'card-title mb-0 text-truncate',
                                currentHistoryKey === item
                                  ? 'text-white'
                                  : 'text-secondary',
                              )}
                            >
                              {formatTime(item)}
                            </p>
                          </div>
                        </div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>

        {isCreate && (
          <div className="col">
            <Create
              queryKey={queryKey}
              data={data}
              currentContent={currentContent}
              currentOverview={currentOverview}
            />
          </div>
        )}

        {currentHistoryKey && (
          <View
            queryKey={queryKey}
            data={data}
            currentHistoryKey={currentHistoryKey}
            diffMatchPatchRef={diffMatchPatchRef}
            hideModal={hideModal}
            setData={setData}
            currentContent={currentContent}
          />
        )}
      </div>
    );
  }

  if (queryAllHistoryByPostIdQuery.error) {
    return <Alert type="error" message={queryAllHistoryByPostIdQuery.error} />;
  }

  return <AlertLoad />;
}

const Create = ({
  queryKey,
  data,
  currentContent,
  currentOverview,
}: {
  queryKey: QueryKey;
  data: IPostEditInfo;
  currentContent: string;
  currentOverview: string;
}) => {
  const [form, setForm] = useState<{
    name: string;
  }>({
    name: '',
  });
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const { show } = useToast();
  const queryClient = useQueryClient();
  const t = useTranslations('PostEditPage');

  const createPostHistoryMutation = useMutation(
    async (variables: TBody<ICreatePostHistoryBody>) => {
      await createPostHistory(variables);
    },
  );

  useEffect(() => {
    setDifferenceData(
      diff(
        {
          name: '',
        },
        form,
      ),
    );
  }, [form]);

  async function onClickCreate(e: FormEvent<HTMLFormElement>) {
    try {
      e.preventDefault();
      e.stopPropagation();

      const body = getFilterData(getDiffData(differenceData));
      const overview = currentOverview;
      const newContext = currentContent;

      if (!newContext) {
        show({
          type: 'SUCCESS',
          message: t('saveCompleted'),
        });
        return;
      }

      await createPostHistoryMutation.mutateAsync({
        id: data.basic.id,
        data: {
          name: body.name,
          overview,
          content: newContext,
        },
      });

      await queryClient.refetchQueries({ queryKey }, { throwOnError: true });
      show({
        type: 'SUCCESS',
        message: t('createVersionCompleted'),
      });
    } catch (e) {
      createPostHistoryMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  function onChange(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  return (
    <div className="row">
      <div className="col">
        <form onSubmit={onClickCreate} className="vstack gap-4">
          <div>
            <label className="form-label">{t('versionName')}</label>
            <input
              name="name"
              type="text"
              className="form-control"
              value={form.name}
              onChange={onChange}
              placeholder={t('pleaseEnterVersionName')}
            />
          </div>

          <button
            type="submit"
            disabled={createPostHistoryMutation.isLoading}
            className="btn btn-primary my-4"
          >
            {createPostHistoryMutation.isLoading && (
              <span
                className="spinner-border spinner-border-sm me-2"
                role="status"
                aria-hidden="true"
              ></span>
            )}
            {t('save')}
          </button>
        </form>
      </div>
    </div>
  );
};

const View = ({
  queryKey,
  data,
  currentHistoryKey,
  diffMatchPatchRef,
  hideModal,
  setData,
  currentContent,
}: {
  queryKey: QueryKey;
  data: IPostEditInfo;
  currentHistoryKey: string | undefined;
  diffMatchPatchRef: MutableRefObject<diff_match_patch | undefined>;
  hideModal: () => void;
  setData: (value: any) => void;
  currentContent: string;
}) => {
  const [loadDiff, setLoadDiff] = useState(false);
  const [isDiff, setIsDiff] = useState(true);
  const [diffText, setDiffText] = useState('');
  const { show } = useToast();
  const [currentHistoryId, setCurrentHistoryId] = useState<number>();
  const t = useTranslations('PostEditPage');

  const queryAllHistoryByPostIdQuery = useQuery(
    ['/forum', '/histories', '/posts', data.basic.id],
    async (context) => {
      return (await queryAllHistoryByPostId({
        id: context.queryKey[3] + '',
      })) as IQueryAllHistoryByPostId;
    },
  );
  const queryHistoryQuery = useQuery(
    ['/forum', '/histories', currentHistoryId],
    async (context) => {
      return (await queryHistory({
        id: context.queryKey[2] + '',
      })) as IHistory;
    },
    {
      enabled: !!currentHistoryId,
    },
  );

  function onClickRestoreHistory() {
    const item = queryHistoryQuery.data;
    if (!item) {
      return;
    }

    setData({
      overview: item.overview || '',
      content: item.content || '',
    });

    show({
      type: 'SUCCESS',
      message: t('restoreHistoryComplete'),
    });
  }

  function onClickItem(id: number) {
    setCurrentHistoryId(id);
  }

  function cleanupContent(content: string = '') {
    return sanitizeHtml(content, {
      allowedTags: [],
    });
  }

  function onClickDiff() {
    const item = queryHistoryQuery.data;
    if (!item) {
      return;
    }

    if (loadDiff) {
      return;
    }

    if (!diffMatchPatchRef || !diffMatchPatchRef.current) {
      show({
        type: 'DANGER',
        message: t('errorOccurredWhileGettingContentDetails'),
      });
      return;
    }

    const oldContent = item.content;
    if (!oldContent) {
      show({
        type: 'INFO',
        message: t('noDifferenceFound'),
      });
      return;
    }

    const newContent = currentContent;
    if (!newContent) {
      show({
        type: 'INFO',
        message: t('noDifferenceFound'),
      });
      return;
    }

    flushSync(() => {
      setLoadDiff(true);
      const current = diffMatchPatchRef.current!;
      const diffMain = current.diff_main(
        cleanupContent(oldContent),
        cleanupContent(newContent),
      );
      current.diff_cleanupSemantic(diffMain);
      const html = current.diff_prettyHtml(diffMain);
      setDiffText(html);
      setLoadDiff(false);

      if (diffMain.filter((item) => item[0] !== 0).length) {
        setIsDiff(true);
      } else {
        setIsDiff(false);
      }
    });
  }

  if (queryAllHistoryByPostIdQuery.data) {
    const historyList = queryAllHistoryByPostIdQuery.data;

    if (
      !Object.keys(historyList).length ||
      !currentHistoryKey ||
      !historyList[currentHistoryKey].length
    ) {
      return <Alert message={t('noContentVersionHistoryExists')} />;
    }

    return (
      <>
        <div className="col">
          <div className="list-group">
            {historyList[currentHistoryKey].map((item, index) => {
              return (
                <button
                  key={item.id}
                  type="button"
                  className={classNames(
                    'list-group-item list-group-item-action text-truncate',
                    currentHistoryId === item.id ? 'active' : false,
                  )}
                  onClick={onClickItem.bind(this, item.id)}
                >
                  <time dateTime={item.createdOn}>
                    {`${formatTime(item.createdOn)} v${
                      historyList[currentHistoryKey].length - index
                    }`}
                  </time>
                </button>
              );
            })}
          </div>
        </div>

        {currentHistoryId && (
          <div className="col-12">
            <div className="my-4">
              <div className="hstack gap-3">
                <div>
                  <a
                    onClick={onClickDiff}
                    className={classNames(
                      'text-decoration-none cursor-pointer',
                      loadDiff ? null : 'text-decoration-underline-hover',
                    )}
                  >
                    {loadDiff ? (
                      <span
                        className="spinner-border spinner-border-sm me-1"
                        role="status"
                        aria-hidden="true"
                      ></span>
                    ) : (
                      <i className="bi bi-plus-slash-minus me-1"></i>
                    )}
                    {loadDiff ? (
                      <span className="cursor-not-allowed">
                        {t('loadingInProgress')}...
                      </span>
                    ) : (
                      '差异'
                    )}
                  </a>
                </div>
                <div>
                  <a
                    onClick={onClickRestoreHistory}
                    className="text-decoration-none cursor-pointer text-decoration-underline-hover"
                  >
                    <i className="bi bi-box-arrow-in-left me-1"></i>
                    {t('restore')}
                  </a>
                </div>
              </div>
              <div className="mt-2 text-muted form-text text-truncate">
                <i className="bi bi-info-circle me-2"></i>
                {t('clickingRestoreWillReplaceCurrentEditingContent')}
              </div>
            </div>

            {queryHistoryQuery.data && (
              <div className="card">
                <div className="card-header">
                  <div className="d-flex align-items-center justify-content-between">
                    <div className="vstack gap-2">
                      <div
                        className="text-wrap"
                        title={queryHistoryQuery.data.name}
                      >
                        {queryHistoryQuery.data.name}
                      </div>
                      <div>{formatDate(queryHistoryQuery.data.createdOn)}</div>
                    </div>
                  </div>
                </div>
                <div className="card-body">
                  <div className="vstack gap-4">
                    {queryHistoryQuery.data.overview && (
                      <div className="card">
                        <div className="card-header">{t('overview')}</div>
                        <div className="card-body">
                          <div
                            dangerouslySetInnerHTML={{
                              __html: queryHistoryQuery.data.overview || '',
                            }}
                          ></div>
                        </div>
                      </div>
                    )}

                    {queryHistoryQuery.data.content && (
                      <div className="card">
                        <div className="card-header">{t('content')}</div>
                        <div className="card-body">
                          <div
                            className="clearfix content-layer text-break"
                            dangerouslySetInnerHTML={{
                              __html: queryHistoryQuery.data.content || '',
                            }}
                          ></div>
                        </div>
                      </div>
                    )}

                    {diffText && (
                      <div className="card">
                        <div className="card-header">
                          {t('difference')}
                          {isDiff ? (
                            <i className="bi bi-plus-slash-minus ms-1 text-danger"></i>
                          ) : (
                            <i className="bi bi-plus-slash-minus ms-1"></i>
                          )}
                        </div>
                        <div className="card-body">
                          <div
                            dangerouslySetInnerHTML={{
                              __html: diffText || '',
                            }}
                          ></div>
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              </div>
            )}

            {queryHistoryQuery.isError && (
              <Alert type="error" message={queryHistoryQuery.error} />
            )}

            {queryHistoryQuery.isLoading && <AlertLoad />}
          </div>
        )}
      </>
    );
  }

  if (queryAllHistoryByPostIdQuery.error) {
    return <Alert type="error" message={queryAllHistoryByPostIdQuery.error} />;
  }

  return <AlertLoad />;
};
